<script setup>
import {onBeforeUnmount, onMounted, reactive} from "vue";
import {message} from "@/util/util";

const data = reactive({
  src: null,
  imgHeight: "300px",
  selectFile: false,
  selectFileName: "",
  selectFileType: "",
});

async function selectFile() {
  const pickerOpts = {
    types: [
      {
        description: "Images",
        accept: {
          "image/*": [".png", ".jpeg", ".jpg"],
        },
      },
    ],
    excludeAcceptAllOption: true,
    multiple: false,
  };
  try {
    const fileHandle = await window.showOpenFilePicker(pickerOpts);
    const file = await fileHandle[0].getFile();
    data.selectFileName = file.name;
    data.selectFileType = file.type;

    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function (e) {
      data.src = e.target.result;
      data.selectFile = true;

      image = new Image();
      image.src = e.target.result;
      setTimeout(() => {
        data.imgHeight = image.height + "px";

        rectangle = document.getElementsByClassName("rectangle")[0];
        rectangleWidth = rectangle.clientWidth;
        rectangleHeight = rectangle.clientHeight;

        imgWidth = image.width;
        imgHeight = image.height;

        rectangle.style.left = (imgWidth - rectangleWidth) / 2 + "px";
        rectangle.style.top = (imgHeight - rectangleHeight) / 2 + "px";

        context.drawImage(image, (image.width / 2 - rectangleWidth / 2), (image.height / 2 - rectangleHeight / 2),
            rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);
      }, 0);
    };
  } catch (e) {
    if (!(e.name === 'AbortError' && e.message === 'The user aborted a request.')) {
      throw e;
    }
  }
}

let isDragging = false;
let mouseUpListener;
let imgWidth;
let imgHeight;

let rectangle;
let initialX;
let initialY;
let rectangleWidth;
let rectangleHeight;

let canvas;
let context;
let image;

onMounted(() => {
  mouseUpListener = () => {
    isDragging = false;
  }
  document.addEventListener("mouseup", mouseUpListener);

  canvas = document.getElementsByClassName("canvas")[0];
  context = canvas.getContext("2d");
});

onBeforeUnmount(() => {
  document.removeEventListener("mouseup", mouseUpListener);
});

function dragStart(e) {
  isDragging = true;

  rectangle = document.getElementsByClassName("rectangle")[0];
  rectangleWidth = rectangle.clientWidth;
  rectangleHeight = rectangle.clientHeight;

  initialX = e.clientX - rectangle.offsetLeft;
  initialY = e.clientY - rectangle.offsetTop;
}

function changePos(e) {
  if (!isDragging) {
    return;
  }

  const x = e.clientX - initialX;
  const y = e.clientY - initialY;

  if (x >= 0 && x < imgWidth - rectangleWidth) {
    rectangle.style.left = x + "px";
    context.drawImage(image, x, y, rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);
  }
  if (y >= 0 && y < imgHeight - rectangleHeight) {
    rectangle.style.top = y + "px";
    context.drawImage(image, x, y, rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);
  }
}

const gap = 2;
const minRange = 20;
let centerX;
let centerY;

function wheel(e) {
  if (!centerX) {
    centerX = image.width / 2;
  }
  if (!centerY) {
    centerY = image.height / 2;
  }

  if (e.deltaY > 0) {
    if (rectangleWidth + gap >= image.width || rectangleHeight + gap >= image.height) {
      return;
    }
    if ((centerX - rectangleWidth / 2 - gap < 0) || (centerY - rectangleHeight / 2 - gap < 0)) {
      return;
    }

    rectangleWidth += gap;
    rectangleHeight += gap;
    rectangle.style.width = rectangleWidth + "px";
    rectangle.style.height = rectangleHeight + "px";
  } else {
    if (rectangleWidth - gap < minRange || rectangleHeight - gap < minRange) {
      return;
    }
    rectangleWidth -= gap;
    rectangleHeight -= gap;
    rectangle.style.width = rectangleWidth + "px";
    rectangle.style.height = rectangleHeight + "px";
  }

  context.drawImage(image, (centerX - rectangleWidth / 2), (centerY - rectangleHeight / 2), rectangleWidth,
      rectangleHeight, 0, 0, canvas.width, canvas.height);
}

function getCanvasFile(resolve) {
  if (!data.selectFile) {
    message("请先选择图片", "warning");
    return;
  }
  canvas.toBlob((blob) => {
    let formData = new FormData();
    const file = new File([blob], data.selectFileName, {type: data.selectFileType})
    formData.append("file", file);
    resolve(formData);
  }, data.selectFileType);
}

defineExpose({
  getCanvasFile,
});
</script>

<template>
  <div class="avatar-container">
    <div class="img-container">
      <div v-if="!data.selectFile" class="select-file" @click="selectFile">
        <p>jpg/png file with a size less than 5MB<em>click to upload</em></p>
      </div>
      <img v-if="data.selectFile" :src="data.src" :style="{'height' : data.imgHeight }" alt="" class="img"
           draggable="false"/>
      <div v-if="data.selectFile" class="rectangle" @mousedown="dragStart($event)" @mousemove="changePos($event)"
           @mousewheel="wheel($event)"></div>
    </div>

    <canvas class="canvas" height="100" width="100"></canvas>
  </div>
</template>

<style lang="scss" scoped>
.avatar-container {
  margin: 0 auto;
  width: fit-content;
  user-select: none;
  display: flex;
  justify-content: center;
  align-items: center;

  .img-container {
    position: relative;
    width: fit-content;

    .select-file {
      width: 500px;
      height: 300px;
      border: 1px dashed #dcdfe6;
      border-radius: 20px;
      display: flex;
      justify-content: center;
      align-items: center;

      &:hover {
        border: 1px dashed #409eff;
        cursor: pointer;
      }

      p {
        font-size: 14px;
        color: #606266;

        em {
          color: #409eff;
          font-style: normal;
          margin-left: 5px;
        }
      }
    }

    .img {
      border: 1px dashed #409eff;
      height: 300px;
    }

    .rectangle {
      width: 100px;
      height: 100px;
      border: 1px dashed #409eff;
      position: absolute;
      z-index: 999;
      cursor: pointer;
    }
  }

  .canvas {
    margin-left: 30px;
    border: 1px dashed #409eff;
    float: left;
    border-radius: 50%;
  }
}
</style>